package com.region.http.client.process;

import com.region.common.adapter.logging.LoggerAdapter;
import com.region.common.adapter.logging.LoggerAdapterFactory;
import com.region.http.client.core.ClientProxyHandler;
import com.region.http.client.core.ProtectorMechanism;
import com.region.http.client.model.RequestMeta;
import com.region.http.client.model.RequestParameter;
import com.region.http.client.model.ResponseData;
import com.region.http.client.request.HttpExecutor;
import com.region.http.client.resolver.parameter.ParameterResolver;
import com.region.http.client.resolver.result.ResultResolver;
import com.region.loadbalancer.LoadBalancerManager;
import com.region.loadbalancer.exception.NoLoadBalancerException;
import com.region.loadbalancer.group.Server;

import java.util.List;

/**
 * @author liujieyu
 * @date 2023/7/8 22:42
 * @description
 */
public class MultiExecutorProcess extends AbstractExecutorProcess {

    private static final LoggerAdapter loggerAdapter = LoggerAdapterFactory.getLogger(MultiExecutorProcess.class);

    private LoadBalancerManager manager;

    public MultiExecutorProcess(List<ParameterResolver> parameterResolvers, List<ResultResolver> resultResolvers,
                                ClientProxyHandler handler, ProtectorMechanism protectorMechanism, LoadBalancerManager manager) {
        super(parameterResolvers, resultResolvers, handler, protectorMechanism);
        this.manager = manager;
    }

    @Override
    public ResponseData execute(RequestMeta requestMeta, HttpExecutor executor, RequestParameter parameter) throws Throwable {
        while (true) {
            Server server = manager.chooseServer(requestMeta.getGroupName());
            if (server == null) {
                // no server
                throw new NoLoadBalancerException("No load-balancing services found, the server name is " + requestMeta.getGroupName());
            }
            long startTime = System.nanoTime();
            try {
                ResponseData responseData = executor.execute(wrappingUrl(disposeAddress(handler.getRealAddress(requestMeta), server),
                        requestMeta, parameter.getPrimitiveArgs()), requestMeta.getRequestAnnotationConfig(), parameter);
                manager.saveServerResponseTime(requestMeta.getGroupName(), server, 1.0 * (System.nanoTime() - startTime) / (1000 * 1000));
                return responseData;
            } catch (Throwable throwable) {
                manager.submitAnomalyServer(requestMeta.getGroupName(), server);
                loggerAdapter.error("The server execute error.", throwable);
            }
        }
    }

    /**
     * Handle address by server
     *
     * @param address
     * @param server
     * @return
     */
    private String disposeAddress(String address, Server server) {
        String protocol;
        int protocolIndex = address.indexOf("://");
        if (protocolIndex == -1) {
            protocol = server.getProtocol() + "://";
        } else {
            protocol = address.substring(0, protocolIndex + 3);
        }
        StringBuilder result = new StringBuilder();
        result.append(protocol);
        result.append(server.getSocketInfo());
        if (protocolIndex != -1) {
            address = address.substring(address.indexOf("://") + 3);
        }
        result.append(address.substring(address.indexOf("/")));
        return result.toString();
    }

}
